﻿using System.Collections.Generic;
using UnityEngine;

namespace CWM.Skinn
{
    public static partial class SkinnContextCommands
    {
        [CtxMenuItem("Renderer/SubMesh/Flip/Flip Faces", 
            "Flips the faces, normals, and tangents of a mesh(s).",
            ContextRequirements.None , 0)]
        public static void RendererFlipFaces()
        {
            foreach (var item in SkinnContext.GetContextItems())
            {
                var context = item.GetComponent<Renderer>();
                if (SkinnEx.IsNullOrNotInAScene(context)) continue;
                if (!SkinnEx.HasSharedMesh(context)) continue;

                var sharedMesh = context.GetSharedMesh();
                var mesh = sharedMesh.Clone() as Mesh;

                var subMeshCount = sharedMesh.subMeshCount;
                for (int i = 0; i < subMeshCount; i++)
                {
                    var indices = sharedMesh.GetTriangles(i);
                    var triangles = new int[indices.Length];
                    for (int ii = 0; ii < indices.Length; ii += 3)
                    {
                        triangles[ii + 0] = indices[ii + 2];
                        triangles[ii + 1] = indices[ii + 1];
                        triangles[ii + 2] = indices[ii + 0];
                    }
                    mesh.SetTriangles(triangles, i);
                }

                var normals = mesh.normals;
                var tangents = mesh.tangents;

                var hasNormals = !SkinnEx.IsNullOrEmpty(normals, mesh.vertices.Length);
                var hasTangents = !SkinnEx.IsNullOrEmpty(tangents, mesh.vertices.Length);

                if(hasNormals || hasTangents)
                    for (int i = 0; i < mesh.vertices.Length; i++)
                    {
                        if (hasNormals) normals[i] *= -1f;
                        if (hasTangents)
                        {
                            Vector3 tangent = tangents[i];
                            tangent *= -1f;
                            tangents[i] = new Vector4(tangent.x, tangent.y, tangent.z, tangents[i].w > 0f ? 0f : 1f);
                        }
                    }
                
                if (hasNormals) mesh.normals = normals;
                if (hasTangents) mesh.tangents = tangents;

                SkinnContext.UndoRecord(context, "Flip Faces");
                context.SetSharedMesh(mesh);
            }

            SkinnContext.Finished();
        }

        [CtxMenuItem("Renderer/SubMesh/Flip/Add Flipped Faces",
            "Adds flipped faces to a mesh's triangles to render back-faces." +
            "Does not increase the vertex count, and checks to for existing triangles.",
            ContextRequirements.None, 0)]
        public static void RendererAddFlipFaces()
        {
            foreach (var item in SkinnContext.GetContextItems())
            {
                var context = item.GetComponent<Renderer>();
                if (SkinnEx.IsNullOrNotInAScene(context)) continue;
                if (!SkinnEx.HasSharedMesh(context)) continue;

                var sharedMesh = context.GetSharedMesh();
                var mesh = sharedMesh.Clone() as Mesh;
                var subMeshCount = sharedMesh.subMeshCount;

                for (int i = 0; i < subMeshCount; i++)
                {
                    var indices = sharedMesh.GetTriangles(i);

                    bool canFlip = false;
                    if (indices.Length % 2 == 0)
                    {
                        int halfLength = indices.Length / 2;
                        for (int ii = 0; ii < halfLength; ii += 3)
                        {
                            if (indices[ii + 0] != indices[halfLength + ii + 2]) { canFlip = true; break; }
                            if (indices[ii + 2] != indices[halfLength + ii + 0]) { canFlip = true; break; }
                        }
                    }
                    else canFlip = true;

                    if (!canFlip)
                    {
                        var warning = "faces already flipped! Mesh{0}, sub-mesh: {1}. skipping...";
                        SkinnContext.LogWarning(string.Format(warning, SkinnEx.EnforceString(mesh.name), i)); continue;
                    }

                    var triangles = new int[indices.Length];
                    for (int ii = 0; ii < indices.Length; ii += 3)
                    {
                        triangles[ii + 0] = indices[ii + 2];
                        triangles[ii + 1] = indices[ii + 1];
                        triangles[ii + 2] = indices[ii + 0];
                    }
                    var mergedIndices = new List<int>(indices);
                    mergedIndices.AddRange(triangles);
                    mesh.SetTriangles(mergedIndices, i);
                }

                SkinnContext.UndoRecord(context, "Flip Faces");
                context.SetSharedMesh(mesh);
            }
            SkinnContext.Finished();
        }
    }
}